﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Web;
using System.Web.Caching;

namespace Dimok {

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>	Exception for signalling locker errors. </summary>
	///
	/// <remarks>	Dimon, 12/21/2010. </remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////

	public class LockerException : ApplicationException {

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="applID">	Identifier for the appl. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public LockerException(int applID) : base(BuildMessage(applID)) { }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Builds a message. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="applID">	Identifier for the appl. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private static string BuildMessage(int applID) {
			LockerTag tag = Locker.GetTag(applID);
			TimeSpan ts = tag.Expiration.Subtract(DateTime.Now);
			return tag == null ? "Unknown concurrency error with applicant " + applID :
				"Applicant " + applID + " is locked by " + tag.UserName + "." + Environment.NewLine +
				"It will expire in " + ts.Minutes + " minutes and " + ts.Seconds + " seconds." + Environment.NewLine +
				"Use [Next] button or refresh this window to see updated information."
				;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>	Locker tag. </summary>
	///
	/// <remarks>	Dimon, 12/21/2010. </remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////

	public class LockerTag {
		public string TimeTag;
		public string UserName;
		public string PageName;
		public DateTime Expiration;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Default constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public LockerTag() { }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="timeTag">		The time tag. </param>
		/// <param name="userName">		Name of the user. </param>
		/// <param name="pageName">		Name of the page. </param>
		/// <param name="expiration">	Date/Time of the expiration. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public LockerTag(string timeTag, string userName, string pageName, DateTime expiration) {
			TimeTag = timeTag;
			UserName = userName;
			PageName = pageName;
			Expiration = expiration;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	
		/// Returns a <see cref="T:System.String" /> that represents the current <see cref="T:
		/// System.Object" />. 
		/// </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <returns>	
		/// A <see cref="T:System.String" /> that represents the current <see cref="T:System.Object" />. 
		/// </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public override string ToString() {
			string[] s = new string[] { "TimeTag:[" + TimeTag + "]", "UserName:[" + UserName + "]", "PageName:[" + PageName + "]", "Expiration in:[" + ((TimeSpan)Expiration.Subtract(DateTime.Now)).ToString() + "]" };
			return string.Join("<br>", s);
		}
	}
	/// <summary>
	/// Manage concurent access to Applicants from different users from different pages
	/// </summary>
	public class Locker {

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the time tag. </summary>
		///
		/// <value>	The time tag. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static string TimeTag { get { return DateTime.Now.ToString("yyMMdd.HHmmss.fffffff"); } }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the name of the user. </summary>
		///
		/// <value>	The name of the user. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private static string UserName { 
			get { try { return HttpContext.Current.User.Identity.Name.Split('\\')[1]; } catch { return HttpContext.Current.User.Identity.Name; } } 
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the name of the page session. </summary>
		///
		/// <value>	The name of the page session. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static string PageSessionName {
			get {
				return HttpContext.Current.Request.UrlReferrer == null ? HttpContext.Current.Request.Url.PathAndQuery : HttpContext.Current.Request.UrlReferrer.PathAndQuery;
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Get LockerTag object from Cache. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="applID">	. </param>
		///
		/// <returns>	LockerTag object. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		internal static LockerTag GetTag(int applID) {
			object tag = HttpContext.Current.Cache[applID+""];
			return tag == null ? null : (LockerTag)tag;
		}
		//http://www.odddpost.com

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Query if 'applID' is locked. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="applID">	. </param>
		///
		/// <returns>	true if locked, false if not. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static bool IsLocked(int applID) { return GetTag(applID) != null; }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Query if 'applID' is locked by other. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="applID">	. </param>
		///
		/// <returns>	true if locked by other, false if not. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static bool IsLockedByOther(int applID) {
			LockerTag ct = GetTag(applID);
			return ct != null && ct.UserName != UserName;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Removes the lock described by applID. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="applID">	. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static void RemoveLock(int applID) {
			HttpContext.Current.Cache.Remove(applID + "");
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Locked data set. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="ShowAll">	true to show, false to hide all. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static LocksDS LockedDataSet(bool ShowAll) { return LockedDataSet(ShowAll, ""); }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Locked data set. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="ShowAll">	true to show, false to hide all. </param>
		/// <param name="Filter">		The filter. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static LocksDS LockedDataSet(bool ShowAll,string Filter) {
			LocksDS.LocksDataTable dt = new LocksDS().Locks;
			foreach (DictionaryEntry de in HttpContext.Current.Cache) {
				LockerTag lt = de.Value as LockerTag;
				if (lt != null && (ShowAll || lt.UserName != UserName)) {
					TimeSpan ts = lt.Expiration.Subtract(DateTime.Now);
					dt.AddLocksRow(lt.UserName, de.Key + "", lt.Expiration, string.Format("{0:00}:{1:00}:{2:00}", ts.Hours, ts.Minutes, ts.Seconds));
				}
			}
			LocksDS ds = new LocksDS();
			ds.Merge(dt.Select(Filter));
			return ds;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Locked by. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="applID">	. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static LocksDS.LocksDataTable LockedBy(int applID) {
			return LockedDataSet(true, "ApplID='" + applID + "' AND User<>'"+UserName+"'").Locks; 
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the locked list. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static string[] LockedList() {
			List<string> lst = new List<string>();
			foreach (DictionaryEntry de in HttpContext.Current.Cache) {
				LockerTag lt = de.Value as LockerTag;
				if (lt != null && lt.UserName != UserName )
					lst.Add(de.Key + "");
			}
			return lst.ToArray();
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Un lock others. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="applID">	. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static void UnLockOthers(int applID) {
			foreach (DictionaryEntry de in HttpContext.Current.Cache) {
				LockerTag lt =de.Value as LockerTag;
				if (lt != null && lt.UserName == UserName && de.Key + "" != applID + "")
					HttpContext.Current.Cache.Remove(de.Key + "");
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Lock ApplID if possible. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="applID">		. </param>
		/// <param name="seconds">	The seconds. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static void LockApplicant(int applID, int seconds) {
			LockApplicant(applID, new TimeSpan(0, 0, seconds));
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Lock ApplID if possible. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <exception cref="LockerException">			Thrown when locker. </exception>
		/// <exception cref="ApplicationException">	Thrown when application. </exception>
		///
		/// <param name="applID">			. </param>
		/// <param name="expiration">	. </param>
		///
		/// ### <returns>	PageSessionID - used on a client side. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static void LockApplicant(int applID, TimeSpan expiration) {
			UnLockOthers(applID);
			if (applID == 0) return;
			string sApplID = applID + "";
			LockerTag concTag = GetTag(applID);
			if (concTag != null) {
				if (concTag.UserName != UserName) throw new LockerException(applID);
			} else concTag = new LockerTag();
			concTag.TimeTag = TimeTag;
			concTag.UserName = UserName.ToLower() == "dimon" && (applID+"").EndsWith("0")? "Dimok" : UserName;
			concTag.PageName = PageSessionName;
			concTag.Expiration = DateTime.Now.Add(expiration);

			HttpContext.Current.Cache.Insert(
				applID.ToString(),
				concTag,
				null,
				concTag.Expiration,
				TimeSpan.Zero,
				CacheItemPriority.NotRemovable,
				null);
			if (HttpContext.Current.Cache[applID + ""] == null)
				HttpContext.Current.Cache.Add(
					applID.ToString(),
					concTag,
					null,
					concTag.Expiration,
					TimeSpan.Zero,
					CacheItemPriority.NotRemovable,
					null);
			if (HttpContext.Current.Cache[applID + ""] == null) throw new ApplicationException("Appl:" + applID + " did not make it to cache.");
		}

		// public static bool IsManagedMenu(int menuID){
		// 	return (bool)SQLHelperEx.ExecuteScalarEx(new UserObject().db,"SELECT IsManaged FROM v_Menu
		// 	WHERE MenuID=@MenuID",menuID);
		// }
		// public static TimeSpan GetApplLockTimeOut(int menuID){
		// 	object timeOut = SQLHelperEx.ExecuteScalarEx(new UserObject().db,"SELECT ApplLockTimeOut
		// 	FROM v_Menu WHERE MenuID=@MenuID",menuID);
		// 	return timeOut==System.DBNull.Value ? ConfigApp.ApplLockTimeOut : new TimeSpan(0,0,
		// 	(int)timeOut);
		// } 
	}
}
